# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Gitlab::BackgroundMigration::BackfillUuidConversionColumnInVulnerabilityOccurrences, schema: 20230629095819, feature_category: :vulnerability_management do # rubocop:disable Layout/LineLength
  let(:namespaces) { table(:namespaces) }
  let(:projects) { table(:projects) }
  let(:users) { table(:users) }
  let(:members) { table(:members) }
  let(:vulnerability_identifiers) { table(:vulnerability_identifiers) }
  let(:vulnerability_scanners) { table(:vulnerability_scanners) }
  let(:vulnerability_findings) { table(:vulnerability_occurrences) }
  let!(:user) { create_user(email: "test1@example.com", username: "test1") }
  let!(:namespace) { namespaces.create!(name: "test-1", path: "test-1", owner_id: user.id) }
  let!(:project) do
    projects.create!(
      id: 9999, namespace_id: namespace.id,
      project_namespace_id: namespace.id,
      creator_id: user.id
    )
  end

  let!(:membership) do
    members.create!(access_level: 50, source_id: project.id, source_type: "Project", user_id: user.id, state: 0,
      notification_level: 3, type: "ProjectMember", member_namespace_id: namespace.id)
  end

  let(:scanner) { create_scanner(project) }
  let(:null_uuid) { '00000000-0000-0000-0000-000000000000' }

  let(:migration_attrs) do
    {
      start_id: finding_with_no_converted_uuid_1.id,
      end_id: finding_with_converted_uuid.id,
      batch_table: :vulnerability_occurrences,
      batch_column: :id,
      sub_batch_size: 100,
      pause_ms: 0,
      connection: ApplicationRecord.connection
    }
  end

  describe "#perform" do
    subject(:background_migration) { described_class.new(**migration_attrs).perform }

    before do
      # We have to disable it in tests because it's triggered BEFORE INSERT OR UPDATE ON vulnerability_occurrences
      # so it's hard to create or update the uuid_convert_string_to_uuid with null UUID value

      # In reality the UPDATE query in the batched background migration could be SET id = id which would
      # trigger UUID update trigger but we can't exactly do that and expect readable tests
      ApplicationRecord.connection.execute("ALTER TABLE vulnerability_occurrences DISABLE TRIGGER trigger_1a857e8db6cd")
    end

    let(:finding_with_no_converted_uuid_1) do
      create_finding(project, scanner, uuid_convert_string_to_uuid: null_uuid)
    end

    let(:finding_with_converted_uuid) do
      uuid = SecureRandom.uuid
      create_finding(project, scanner, uuid: uuid, uuid_convert_string_to_uuid: uuid)
    end

    after do
      ApplicationRecord.connection.execute("ALTER TABLE vulnerability_occurrences ENABLE TRIGGER trigger_1a857e8db6cd")
    end

    it "backfills the uuid_convert_string_to_uuid column" do
      expect { background_migration }.to change { finding_with_no_converted_uuid_1.reload.uuid_convert_string_to_uuid }
        .from(null_uuid).to(finding_with_no_converted_uuid_1.uuid)
    end

    it "doesn't change the UUID for exisiting records" do
      expect { background_migration }.not_to change { finding_with_converted_uuid.uuid_convert_string_to_uuid }
    end
  end

  private

  def create_scanner(project, overrides = {})
    attrs = {
      project_id: project.id,
      external_id: "test_vulnerability_scanner",
      name: "Test Vulnerabilities::Scanner"
    }.merge(overrides)

    vulnerability_scanners.create!(attrs)
  end

  def create_identifier(project, overrides = {})
    attrs = {
      project_id: project.id,
      external_id: "CVE-2018-1234",
      external_type: "CVE",
      name: "CVE-2018-1234",
      fingerprint: SecureRandom.hex(20)
    }.merge(overrides)

    vulnerability_identifiers.create!(attrs)
  end

  def create_finding(project, scanner, overrides = {})
    attrs = {
      project_id: project.id,
      scanner_id: scanner.id,
      severity: 5, # medium
      confidence: 2, # unknown,
      report_type: 99, # generic
      primary_identifier_id: create_identifier(project).id,
      project_fingerprint: SecureRandom.hex(20),
      location_fingerprint: SecureRandom.hex(20),
      uuid: SecureRandom.uuid,
      name: "CVE-2018-1234",
      raw_metadata: "{}",
      metadata_version: "test:1.0"
    }.merge(overrides)

    vulnerability_findings.create!(attrs)
  end

  def create_user(overrides = {})
    attrs = {
      email: "test@example.com",
      notification_email: "test@example.com",
      name: "test",
      username: "test",
      state: "active",
      projects_limit: 10
    }.merge(overrides)

    users.create!(attrs)
  end
end
